#ifndef PERMUTACION_CU
#define PERMUTACION_CU

// Includes
#include <iostream>
#include <string.h>

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "permutacion.cuh"

#include "../utils/cuPrintf.cuh"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "../utils/Random.cu"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "../utils/global.cuh"

//Incluyo la clase permutacionCPU.cu para procesar las islas en CPU
#include "permutacionCPU.cu"

//Incluyo la clase donde se calcula el fitness
#include "../fitness/fitnessPermutacion.cu"

//Incluyo la clase donde se inicializa la poblacion manualmente
#include "../inicializacion/initPermutacion.cu"
#include "../inicializacion/initPermutacionCPU.cu"

using namespace std;

/****************** Representacion Permutacion ********************/

/******************* Implementacion de Funciones del Device *****************/

__global__ void inicializarPoblacionPermutacion(int* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice, float* matrizCostosDevice){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	//inicializo la poblacion en -1
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		poblacion[posicionCromosoma+i] = -1;
	}
	int posicionPoblacion = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, indiceRandom)% cantGenesEnCromosomaDevice);
		while(poblacion[posicionPoblacion+threadIdx.y*2+r*cantCromosomasPoblacionDevice] != -1){
			r = (r+1) % cantGenesEnCromosomaDevice;
		}
		poblacion[posicionPoblacion+threadIdx.y*2+r*cantCromosomasPoblacionDevice] = i;
	}

	for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
		//Genero valor aleatorio.
		unsigned int r = (getRandomInt(randomGPU, indiceRandom)% cantGenesEnCromosomaDevice);
		while(poblacion[posicionPoblacion+threadIdx.y*2+1+r*cantCromosomasPoblacionDevice] != -1){
			r = (r+1) % cantGenesEnCromosomaDevice;
		}
		poblacion[posicionPoblacion+threadIdx.y*2+1+r*cantCromosomasPoblacionDevice] = i;
	}
	fitnessActualDevice[posicionFitness] = calcularFitnessPermutacionGPU(poblacion, posicionPoblacion+threadIdx.y*2, matrizCostosDevice);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessPermutacionGPU(poblacion, posicionPoblacion+threadIdx.y*2+1, matrizCostosDevice);
	
	fitnessOrdenadoDevice[posicionFitness] = fitnessActualDevice[posicionFitness];
	fitnessOrdenadoDevice[posicionFitness + 1] = fitnessActualDevice[posicionFitness + 1];

}

__global__ void mainKernelPermutacion(int* poblacionActualDevice, int* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int* nuevaPoblacionAcambiarDevice, int* individuosAMigrarDevice, float* probabilidadRuletaDevice, float* fitnessTotalParaRuletaDevice, struct Random r, int * individuoAMigrarGPUaCPUdevice, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado, int iteracion, int* mejorIndividuoGPUDevice, float* mejorFitnessIndividuoGPUDevice, float* matrizCostosDevice){
	if(tipoSeleccionDevice == 2){
		asignarProbabilidadRuletaPermutacion(fitnessActualDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r);
	}
	//genero numero aleatorio entre 0 y cant individuos por poblacion
	int posicionCromosoma1 = seleccionPermutacion(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	int posicionCromosoma2 = seleccionPermutacion(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado);
	cruzamientoRepresentacionPermutacion(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamientoDevice, r, tipoCruzamientoDevice);
	mutacionPermutacion(nuevaPoblacionDevice, probabilidadMutacionDevice, r, tipoMutacionDevice);
	asignarFitnessPermutacion(nuevaPoblacionDevice, nuevoFitnessDevice, fitnessOrdenadoDevice, matrizCostosDevice);
	reemplazoPermutacion(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, fitnessActualDevice, nuevoFitnessDevice, tipoReemplazoDevice);
	if((iteracion + 1) % generacionesMigrarDevice == 0 || imprimirPoblacionEnteraDevice == 0){
		__syncthreads();
		if(threadIdx.y == 0){
			if((iteracion + 1) % generacionesMigrarDevice == 0)
				individosAMigrarRepresentacionPermutacion(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
			if(imprimirPoblacionEnteraDevice == 0){
				//obtengo el mejor individuo
				int posMejor = obtenerMejorIndividuoPermutacion(nuevoFitnessDevice);
				for(int i = 0; i < cantGenesEnCromosomaDevice; i++){
					mejorIndividuoGPUDevice[i + blockIdx.y * cantGenesEnCromosomaDevice] = nuevaPoblacionDevice[posMejor+i*cantCromosomasPoblacionDevice];
				}
				//asignar mejor fitness
				mejorFitnessIndividuoGPUDevice[blockIdx.y] = nuevoFitnessDevice[(posMejor-(posMejor%cantGenesEnCromosomaDevice)+((posMejor%cantGenesEnCromosomaDevice)*cantGenesEnCromosomaDevice))/cantGenesEnCromosomaDevice];
			}
		}
	}
}

__global__ void obtenerMejoresIndividuosPermutacion(int* poblacion, float* fitness, int* mejorIndividuoDevice, float* mejorFitnessIndividuoGPUDevice){
	//obtengo el mejor individuo
	int posMejor = obtenerMejorIndividuoPermutacion(fitness);
	for(int i = 0; i < cantGenesEnCromosomaDevice; i++){
		mejorIndividuoDevice[i + blockIdx.y * cantGenesEnCromosomaDevice] = poblacion[posMejor+i*cantCromosomasPoblacionDevice];
	}
	//asignar mejor fitness
	mejorFitnessIndividuoGPUDevice[blockIdx.y] = fitness[(posMejor-(posMejor%cantGenesEnCromosomaDevice)+((posMejor%cantGenesEnCromosomaDevice)*cantGenesEnCromosomaDevice))/cantGenesEnCromosomaDevice];
}

/******************* Implementacion de Funciones de Migracion *****************/

//Obtiene el mejor individuo de una poblacion (1 bloque por poblacion)
//selecciona el mejor elemento
__device__ int obtenerMejorIndividuoPermutacion(float* vectorFitness){
	float mejor = INT_MIN;
	int indiceMejor;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria+i];
		if(valorFitness > mejor){
			mejor = valorFitness;
			indiceMejor = i;
		}
	}
	int posicionCromosomaMejor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor;
	return posicionCromosomaMejor;
}

//Obtiene el indice del peor individuo y del fitness del peor individuo de una poblacion (1 bloque por poblacion) y retorna por referencia en el arreglo indices
__device__ void obtenerPeorIndividuoPermutacion(float* vectorFitness, int indices[2]){
	float peor = INT_MAX;
	int indicePeor;
	float valorFitness;
	int posicionCromosomaPeor;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria+i];
		if(valorFitness < peor){
			peor = valorFitness;
			indicePeor = i;
		}
	}
	posicionCromosomaPeor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indicePeor;
	indices[0] = posicionCromosomaPeor;
	indices[1] = indicePeor;
}

__global__ void migracionRepresentacionPermutacion(int* poblacionActualDevice, float* fitnessActualDevice, int* individuosAMigrarDevice, struct Random randomGPU, int* individuoAMigrarCPUaGPUdevice, float* matrizCostosDevice){
	int indices[2];
	obtenerPeorIndividuoPermutacion(fitnessActualDevice, indices);
	int posicionCromosomaEmigrante = indices[0];
	int posicionCromosomaInmigrante;
	int aux;

	if(blockIdx.y == 0){
		aux = cantidadPoblacionesGPUDevice - 1;
	}else{
		aux = (blockIdx.y - 1) % cantidadPoblacionesGPUDevice;
	}
	posicionCromosomaInmigrante = aux * cantGenesEnCromosomaDevice;
	if(blockIdx.y == 0 && cantidadPoblacionesCPUDevice > 0){
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			poblacionActualDevice[posicionCromosomaEmigrante+i*cantCromosomasPoblacionDevice] = individuoAMigrarCPUaGPUdevice[i];
		}
	}else{
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			poblacionActualDevice[posicionCromosomaEmigrante+i*cantCromosomasPoblacionDevice] = individuosAMigrarDevice[posicionCromosomaInmigrante+i];
		}
	}
	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice + indices[1];
	fitnessActualDevice[posicionFitness] = calcularFitnessPermutacionGPU(poblacionActualDevice, posicionCromosomaEmigrante, matrizCostosDevice);
}


__device__ void individosAMigrarRepresentacionPermutacion(float* nuevoFitnessDevice, int* nuevaPoblacionDevice, int* individuosAMigrarDevice, int* individuoAMigrarGPUaCPUdevice){
	int posicionMejorIndividuo = obtenerMejorIndividuoPermutacion(nuevoFitnessDevice);
	if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice - 1){
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			individuoAMigrarGPUaCPUdevice[i] = nuevaPoblacionDevice[posicionMejorIndividuo+i*cantCromosomasPoblacionDevice];
		}
	}else{
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			individuosAMigrarDevice[blockIdx.y*cantGenesEnCromosomaDevice + i] = nuevaPoblacionDevice[posicionMejorIndividuo+i*cantCromosomasPoblacionDevice];
		}
	}
}


/******************* Implementacion de Funciones del Device *****************/

__device__ void asignarProbabilidadRuletaPermutacion(float* fitnessActualDevice, float* probabilidadRuletaBinaria, float* fitnessTotalParaRuletaDevice, struct Random randomGPU){
	__syncthreads();
	if(threadIdx.y == 0){
		fitnessTotalParaRuletaDevice[blockIdx.y] = 0;
		for(int i = blockIdx.y*cantCromosomasPoblacionDevice; i < blockIdx.y*cantCromosomasPoblacionDevice + cantCromosomasPoblacionDevice; i++){
			fitnessTotalParaRuletaDevice[blockIdx.y] += fitnessActualDevice[i];
		}
		for(int i = blockIdx.y*cantCromosomasPoblacionDevice; i < blockIdx.y*cantCromosomasPoblacionDevice + cantCromosomasPoblacionDevice; i++){
			probabilidadRuletaBinaria[i] = fitnessActualDevice[i]/fitnessTotalParaRuletaDevice[blockIdx.y] * 100;
		}
	}

}


/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionPermutacion(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado){
	int resultado = -1;
	if(ejecutarFuncion == 0){
		resultado = seleccionPorRangoPermutacion(vectorIndicesFitnessOrdenado, randomGPU);
	}else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoPermutacion(vectorFitness, randomGPU);
		}else{
			if(ejecutarFuncion == 2){
				resultado = seleccionRuedaRuletaPermutacion(probabilidadRuletaDevice, randomGPU);
			}
		}
	}
	return resultado;
}


//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoPermutacion(float* vectorFitness, struct Random randomGPU){
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int r = getRandomInt(randomGPU, indiceRandom)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r;
	r = getRandomInt(randomGPU, indiceRandom)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r;
	if(vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}

//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionPorRangoPermutacion(int* vectorIndicesFitnessOrdenado, struct Random randomGPU){
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int indiceMejor = getRandomInt(randomGPU, indiceRandom)% cantidadIndividuosSeleccionPorRangoDevice;
	int posicionCromosoma = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + vectorIndicesFitnessOrdenado[blockIdx.y * cantCromosomasPoblacionDevice + indiceMejor];
	return posicionCromosoma;
}

//Seleccion por rueda de ruleta
__device__ int seleccionRuedaRuletaPermutacion(float* probabilidadRuletaDevice, struct Random randomGPU){
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	float aux = getRandomInt(randomGPU, indiceRandom);
	float r = fmod(aux, 100);
	float suma = probabilidadRuletaDevice[blockIdx.y*cantCromosomasPoblacionDevice];
	int i = 0;
	while((r > suma) && (i < cantCromosomasPoblacionDevice - 1)){
		i++;
		suma = suma + probabilidadRuletaDevice[blockIdx.y * cantCromosomasPoblacionDevice + i];
	}
	int posicionCromosoma = blockIdx.y * cantCromosomasPoblacionDevice  * cantGenesEnCromosomaDevice + i;
	return posicionCromosoma;
}

/******************* Implementacion de Funciones de Cruzamiento *****************/

__device__ void cruzamientoRepresentacionPermutacion(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0){
		cruzamientoCromosomaPMX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}else{
		if(ejecutarFuncion == 1){
			cruzamientoCromosomaOX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}else{
			if(ejecutarFuncion == 2){
				cruzamientoCromosomaCX(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
		}
	}
}

__device__ void cruzamientoCromosomaPMX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y*2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int prob;
	prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad){
		unsigned int r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		for(int j=0; j<cantGenesEnCromosomaDevice; j++){
			if((j>=r1) && (j <= r2)){
				nuevaPoblacion[posicionNuevoCromosoma1+j*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+j*cantCromosomasPoblacionDevice];
				nuevaPoblacion[posicionNuevoCromosoma2+j*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+j*cantCromosomasPoblacionDevice];
			}else{
				nuevaPoblacion[posicionNuevoCromosoma1+j*cantCromosomasPoblacionDevice] = -1;
				nuevaPoblacion[posicionNuevoCromosoma2+j*cantCromosomasPoblacionDevice] = -1;
			}
		}
		int valor, valorInicial;
		for(int k=r1; k<=r2; k++){
			valor = poblacionActual[posicionCromosoma2+k*cantCromosomasPoblacionDevice];
			valorInicial = valor;
			int pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);

			if((pos < r1) || (pos > r2)){
				valor = poblacionActual[posicionCromosoma1+pos*cantCromosomasPoblacionDevice];
				pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
				while((pos >= r1) && (pos <= r2)){
					valor = poblacionActual[posicionCromosoma1+pos*cantCromosomasPoblacionDevice];
					pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
				}
				nuevaPoblacion[posicionNuevoCromosoma1+pos*cantCromosomasPoblacionDevice] = valorInicial;
			}
		}
		for(int k=r1; k<=r2; k++){
			valor = poblacionActual[posicionCromosoma1+k*cantCromosomasPoblacionDevice];
			valorInicial = valor;
			int pos = dondeEsta(poblacionActual,valor,posicionCromosoma2);
			if((pos < r1) || (pos > r2)){
				valor = poblacionActual[posicionCromosoma2+pos*cantCromosomasPoblacionDevice];
				pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);

				while((pos >= r1) && (pos <= r2)){
					valor = poblacionActual[posicionCromosoma2+pos*cantCromosomasPoblacionDevice];
					pos = dondeEsta(poblacionActual,valor,posicionCromosoma1);
				}

				nuevaPoblacion[posicionNuevoCromosoma2+pos*cantCromosomasPoblacionDevice] = valorInicial;
			}
		}
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			if(nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] == -1)
				nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+i*cantCromosomasPoblacionDevice];
			if(nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] == -1)
				nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+i*cantCromosomasPoblacionDevice];
		}
	}
	else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+i*cantCromosomasPoblacionDevice];
			nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+i*cantCromosomasPoblacionDevice];
		}
	}
}


__device__ void cruzamientoCromosomaOX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad ){
		unsigned int r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		unsigned int r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2<r1){
			unsigned int r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = -1;
		}
		int cont1 = 0;
	    int cont2 = 0;
	    for(int i=r1; i<=r2; i++){
	    	nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+i*cantCromosomasPoblacionDevice];
		    cont1++;

		    nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+i*cantCromosomasPoblacionDevice];
		    cont2++;
	    }
	    int j = (r2+1) % cantGenesEnCromosomaDevice;
		int k = (r2+1) % cantGenesEnCromosomaDevice;
		while(cont1 < cantGenesEnCromosomaDevice){
			if(estaPresente(poblacionActual[posicionCromosoma2 + j*cantCromosomasPoblacionDevice], nuevaPoblacion, posicionNuevoCromosoma1, posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice) == 0){
				nuevaPoblacion[posicionNuevoCromosoma1+k*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2 + j*cantCromosomasPoblacionDevice];
				cont1++;
				k = (k+1) % cantGenesEnCromosomaDevice;
			}
			j = (j+1) % cantGenesEnCromosomaDevice;
		}
		j = (r2+1) % cantGenesEnCromosomaDevice;
		k = (r2+1) % cantGenesEnCromosomaDevice;
		while(cont2 < cantGenesEnCromosomaDevice){
			if(estaPresente(poblacionActual[posicionCromosoma1 + j*cantCromosomasPoblacionDevice], nuevaPoblacion, posicionNuevoCromosoma2, posicionNuevoCromosoma2 + cantGenesEnCromosomaDevice) == 0){
				nuevaPoblacion[posicionNuevoCromosoma2+k*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1 + j*cantCromosomasPoblacionDevice];
				cont2++;
				k = (k+1) % cantGenesEnCromosomaDevice;
			}
			j = (j+1) % cantGenesEnCromosomaDevice;
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+i*cantCromosomasPoblacionDevice];
			nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+i*cantCromosomasPoblacionDevice];
		}
	}
}


__device__ void cruzamientoCromosomaCX(int* poblacionActual, int* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad,struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	int pos, pos2;
	unsigned int prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = -1;
			nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = -1;
		}
		int cont = 0;
		pos = posicionPrimerLibre(nuevaPoblacion, posicionNuevoCromosoma1);
		while(pos != -1){
			if(cont == 0){
				while(nuevaPoblacion[posicionNuevoCromosoma1 + pos*cantCromosomasPoblacionDevice] == -1){
					nuevaPoblacion[posicionNuevoCromosoma1 + pos*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice];
					pos2 = dondeEsta(poblacionActual, poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice], posicionCromosoma2);
					nuevaPoblacion[posicionNuevoCromosoma2 + pos2*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice];
					pos = pos2;
				}
			}else{
				while(nuevaPoblacion[posicionNuevoCromosoma2 + pos*cantCromosomasPoblacionDevice] == -1){
					nuevaPoblacion[posicionNuevoCromosoma2 + pos*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice];
					pos2 = dondeEsta(poblacionActual, poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice], posicionCromosoma2);
					nuevaPoblacion[posicionNuevoCromosoma1 + pos2*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1 + pos*cantCromosomasPoblacionDevice];
					pos = pos2;
				}
			}
			cont = (cont +1) % 2;
			pos = posicionPrimerLibre(nuevaPoblacion, posicionNuevoCromosoma1);
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma1+i*cantCromosomasPoblacionDevice];
			nuevaPoblacion[posicionNuevoCromosoma2+i*cantCromosomasPoblacionDevice] = poblacionActual[posicionCromosoma2+i*cantCromosomasPoblacionDevice];
		}
	}
}

/******************* Implementacion de Funciones de Mutacion *****************/

__device__ void mutacionPermutacion(int* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion){
	if(ejecutarFuncion == 0){
		mutacionPorIntercambio(poblacion, probabilidad, randomGPU);
	}else{
		if(ejecutarFuncion == 1){
			mutacionPorInsercionSimple(poblacion, probabilidad, randomGPU);
		}else{
			if(ejecutarFuncion == 2){
				mutacionPorInversionIVM(poblacion, probabilidad, randomGPU);
			}
		}
	}
}

__device__ void mutacionPorIntercambio(int* poblacion, float probabilidad, struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y  * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad ){
		int r1 = (getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice);
		int r2 = (getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice);
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		int aux = poblacion[posicionNuevoCromosoma1 + r1 * cantCromosomasPoblacionDevice];
		poblacion[posicionNuevoCromosoma1 + r1 * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma1 + r2 * cantCromosomasPoblacionDevice];
		poblacion[posicionNuevoCromosoma1 + r2 * cantCromosomasPoblacionDevice] = aux;
	}
	prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad ){
		int r1 = (getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice);
		int r2 = (getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice);
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		int aux = poblacion[posicionNuevoCromosoma2 + r1 * cantCromosomasPoblacionDevice];
		poblacion[posicionNuevoCromosoma2 + r1 * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma2 + r2 * cantCromosomasPoblacionDevice];
		poblacion[posicionNuevoCromosoma2 + r2 * cantCromosomasPoblacionDevice] = aux;
	}
}

__device__ void mutacionPorInsercionSimple(int* poblacion, float probabilidad, struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int r3;
	int aux;
	unsigned int r1, r2;

	unsigned int prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if( prob < probabilidad ){
		r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		aux = poblacion[posicionNuevoCromosoma1+r2 * cantCromosomasPoblacionDevice];
	    for(int i=r2; i>r1+1; i--){
	    	poblacion[posicionNuevoCromosoma1+i * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma1+(i-1) * cantCromosomasPoblacionDevice];
	    }
	    poblacion[posicionNuevoCromosoma1+(r1+1) * cantCromosomasPoblacionDevice] = aux;
	}
	prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if( prob < probabilidad ){
		r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		aux = poblacion[posicionNuevoCromosoma2+r2 * cantCromosomasPoblacionDevice];
		for(int i=r2; i>r1+1; i--){
			poblacion[posicionNuevoCromosoma2+i * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma2+(i-1) * cantCromosomasPoblacionDevice];
		}
		poblacion[posicionNuevoCromosoma2+(r1+1) * cantCromosomasPoblacionDevice] = aux;
	}
}


__device__ void mutacionPorInversionIVM(int* poblacion, float probabilidad, struct Random randomGPU){
	int posicionNuevoCromosoma1 = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + 1;
	int indiceRandom = threadIdx.y + blockIdx.y * blockDim.y;
	unsigned int r3;
	unsigned int r1, r2;
	int tam, pos2;
	unsigned int prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		tam = r2-r1+1;
		pos2 = r2;
		int swap;
		for(int i=r1; i<r1 + tam/2; i++){
			swap = poblacion[posicionNuevoCromosoma1+i * cantCromosomasPoblacionDevice];
			poblacion[posicionNuevoCromosoma1+i * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma1+pos2 * cantCromosomasPoblacionDevice];
			poblacion[posicionNuevoCromosoma1+pos2 * cantCromosomasPoblacionDevice] = swap;
			pos2--;
		}
	}
	prob = (getRandomInt(randomGPU, indiceRandom) % 101);
	if(prob < probabilidad ){
		r1 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		r2 = getRandomInt(randomGPU, indiceRandom) % cantGenesEnCromosomaDevice;
		if(r2 == r1){
			r2 = (r2 + 1) % cantGenesEnCromosomaDevice;
		}
		if(r2 < r1){
			r3 = r1;
			r1 = r2;
			r2 = r3;
		}
		tam = r2-r1+1;
		pos2 = r2;
		int swap;
		for(int i=r1; i<r1 + tam/2; i++){
			swap = poblacion[posicionNuevoCromosoma2+i * cantCromosomasPoblacionDevice];
			poblacion[posicionNuevoCromosoma2+i * cantCromosomasPoblacionDevice] = poblacion[posicionNuevoCromosoma2+pos2 * cantCromosomasPoblacionDevice];
			poblacion[posicionNuevoCromosoma2+pos2 * cantCromosomasPoblacionDevice] = swap;
			pos2--;
		}
	}
}

/******************* Implementacion de Funciones de fitness *****************/

__device__ void asignarFitnessPermutacion(int* nuevaPoblacion, float* nuevoVectorFitness, float* fitnessOrdenadoDevice, float* matrizCostosDevice){
	//Obtengo posicion del primer cromosoma.
	int posicionCromosoma = threadIdx.y * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	nuevoVectorFitness[posicionFitness] = calcularFitnessPermutacionGPU(nuevaPoblacion, posicionCromosoma, matrizCostosDevice);
	nuevoVectorFitness[posicionFitness + 1] = calcularFitnessPermutacionGPU(nuevaPoblacion, posicionCromosoma + 1, matrizCostosDevice);
	fitnessOrdenadoDevice[posicionFitness] = nuevoVectorFitness[posicionFitness];
	fitnessOrdenadoDevice[posicionFitness + 1] = nuevoVectorFitness[posicionFitness + 1];
}

/******************** Implementacion de Funciones de reemplazo **************/

__device__ void reemplazoPermutacion(int * poblacionActualDevice, int * nuevaPoblacionDevice, int * poblacionActualAcambiar, int * nuevaPoblacionAcambiar, float * fitnessActualDevice, float* nuevoFitnessDevice, int ejecutarFuncion){
	if(ejecutarFuncion == 1){
		reemplazoElitistaPermutacion(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiar, nuevaPoblacionAcambiar, fitnessActualDevice, nuevoFitnessDevice);
	}
}
__device__ void reemplazoElitistaPermutacion(int* poblacionActual, int* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness){
	__syncthreads();
	if(threadIdx.y == 0){
		int cantAremplazar = porcentajeElitismoDevice*cantCromosomasPoblacionDevice/100;
		int peor = INT_MAX;
		int mejor = INT_MIN;
		int posPeor = -1;
		int posMejor = -1;
		int posFitness = blockIdx.y * cantCromosomasPoblacionDevice;
		for(int i = 0; i < cantAremplazar; i++){
			if(fitnessActual[posFitness+i] < peor){
				peor = fitnessActual[posFitness+i];
				posPeor = i;
			}
			if(nuevoFitness[posFitness+i] > mejor){
				mejor = nuevoFitness[posFitness+i];
				posMejor = i;
			}
			poblacionActualAcambiar[posFitness+i] = 1;
			nuevaPoblacionAcambiar[posFitness+i] = 1;
		}

		for(int i = cantAremplazar; i < cantCromosomasPoblacionDevice; i++){
			if(fitnessActual[posFitness+i] > peor){
				poblacionActualAcambiar[posFitness+i] = 1;
				poblacionActualAcambiar[posFitness+posPeor] = 0;
				peor = INT_MAX;
				posPeor = -1;
				for(int j = 0; j <= i; j++){
					if(poblacionActualAcambiar[posFitness+j] == 1){
						if(fitnessActual[posFitness+j] < peor){
							peor = fitnessActual[posFitness+j];
							posPeor = j;
						}
					}
				}
			}else{
				poblacionActualAcambiar[posFitness+i] = 0;
			}
			if(nuevoFitness[posFitness+i] < mejor){
				nuevaPoblacionAcambiar[posFitness+i] = 1;
				nuevaPoblacionAcambiar[posFitness + posMejor] = 0;
				mejor = INT_MIN;
				posMejor = -1;
				for(int j = 0; j <= i; j++){
					if(nuevaPoblacionAcambiar[posFitness+j] == 1){
						if(nuevoFitness[posFitness+j] > mejor){
							mejor = nuevoFitness[posFitness+j];
							posMejor = j;
						}
					}
				}
			}else{
				nuevaPoblacionAcambiar[posFitness+i] = 0;
			}
		}

		for(int i = 0; i < cantAremplazar; i++){
			int j = 0;
			while(poblacionActualAcambiar[posFitness+j] == 0){
				j++;
			}
			int k = 0;
			while(nuevaPoblacionAcambiar[posFitness+k] == 0){
				k++;
			}
			nuevoFitness[posFitness+k] = fitnessActual[posFitness+j];
			int posNuevoCromosoma = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + k;
			int posCromosomaActual = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + j;
			for(int h = 0; h < cantGenesEnCromosomaDevice; h++){
				nuevaPoblacion[posNuevoCromosoma+h * cantCromosomasPoblacionDevice] = poblacionActual[posCromosomaActual+h * cantCromosomasPoblacionDevice];
			}
			poblacionActualAcambiar[posFitness+j] = 0;
			nuevaPoblacionAcambiar[posFitness+k] = 0;
		}
	}
}

/******************* Implementacion de Funciones del host *****************/

void traerImprimirPermutacion(float* fitnessHost, float* fitnessDevice, int tamVectorFitnessFloat, int* poblacionHost, int* poblacionDevice, int tamVectorPoblacion, int iteracion){
	cudaMemcpy(fitnessHost, fitnessDevice, tamVectorFitnessFloat, cudaMemcpyDeviceToHost);
	cudaMemcpy(poblacionHost, poblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	printf("\nPOBLACION EN GPU (iteracion %d):\n",iteracion);
	for(int k = 0; k<cantidadPoblacionesGPU; k++){
		for(int i = 0; i<cantCromosomasPoblacion; i++){
			for(int j = 0; j<cantGenesEnCromosoma; j++){
				printf("%i ", poblacionHost[k*cantCromosomasPoblacion*cantGenesEnCromosoma+j*cantCromosomasPoblacion+i]);
			}
			printf("\n");
		}
	}
	printf("\n\nFITNESS EN GPU:\n\n");
	
	for(int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
		printf("%f ", fitnessHost[i] * (-1));
	}
	printf("\n\n");
}

void traerImprimirMejorIndividuoPermutacion(float* mejorFitnessIndividuoGPU, float* mejorFitnessIndividuoGPUDevice, int tamVectorFitnessMejoresIndividuosGPU, int* mejorIndividuoGPU, int* mejorIndividuoGPUDevice, int tamVectorMejoresIndividuosGPU, int iteracion){
	cudaMemcpy(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, cudaMemcpyDeviceToHost);
	cudaMemcpy(mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, cudaMemcpyDeviceToHost);

	printf("\nMEJOR INDIVIDUO POR POBLACION EN GPU (iteracion %d):\n",iteracion);
	for(int i = 0; i < cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
		if(i%cantGenesEnCromosoma == 0){
			printf("\n");
		}
		printf("%i ", mejorIndividuoGPU[i]);
	}

	printf("\n\nFITNESS MEJOR INDIVIDUO POR POBLACION EN GPU:\n\n");
	for(int i = 0; i < cantidadPoblacionesGPU; i++){
		printf("%f ", mejorFitnessIndividuoGPU[i] * (-1));
	}
	printf("\n\n");
}

void representacionPermutacion(){
	int tamVectorPoblacion = sizeof(int)*totalGenesGPU;
	int tamVectorFitnessFloat = sizeof(float)*totalIndividuosGPU;
	int tamVectorFitnessInt = sizeof(int)*totalIndividuosGPU;
	int tamVectorIndividuosAMigrarGPU = sizeof(int)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorIndividuosAMigrarCPU = sizeof(int)*cantGenesEnCromosoma*cantidadPoblacionesCPU;

	int tamVectorPoblacionParaRuleta = sizeof(float)*totalIndividuosGPU;
	int tamVectorFitnessRuleta = sizeof(float)*cantidadPoblacionesGPU;
	int tamVectorPoblacionParaRuletaCPU = sizeof(float)*totalIndividuosCPU;
	int tamVectorFitnessRuletaCPU = sizeof(float)*cantidadPoblacionesCPU;
	int tamVectorMejoresIndividuosGPU = sizeof(int) * cantGenesEnCromosoma * cantidadPoblacionesGPU;
	int tamVectorMejoresIndividuosCPU = sizeof(int) * cantGenesEnCromosoma * cantidadPoblacionesCPU;
	int tamVectorFitnessMejoresIndividuosCPU = sizeof(float) * cantidadPoblacionesCPU;
	int tamVectorFitnessMejoresIndividuosGPU = sizeof(float) * cantidadPoblacionesGPU;

	static int *poblacionActualHostGPU = (int *) malloc(tamVectorPoblacion);
	static int *nuevaPoblacionHostGPU = (int *) malloc(tamVectorPoblacion);
	static float *fitnessActualHostGPU = (float *) malloc(tamVectorFitnessFloat);
	static float *nuevoFitnessHostGPU = (float *) malloc(tamVectorFitnessFloat);
	static float *probabilidadRuletaHost = (float *) malloc(tamVectorPoblacionParaRuleta);
	static float *fitnessTotalParaRuletaHost = (float *) malloc(tamVectorFitnessRuleta);
	static int *individuosAMigrarHostGPU = (int *) malloc(tamVectorIndividuosAMigrarGPU);
	static int *mejorIndividuoCPU = (int *) malloc(tamVectorMejoresIndividuosCPU);
	static int *mejorIndividuoGPU = (int *) malloc(tamVectorMejoresIndividuosGPU);
	static float *mejorFitnessIndividuoCPU = (float *) malloc(tamVectorFitnessMejoresIndividuosCPU);
	static float *mejorFitnessIndividuoGPU = (float *) malloc(tamVectorFitnessMejoresIndividuosGPU);
	static int *indicesFitnessOrdenadoHost = (int *) malloc(tamVectorFitnessInt);

	int * poblacionActualDevice;
	int * nuevaPoblacionDevice;
	float* fitnessActualDevice;
	float * nuevoFitnessDevice;
	float * fitnessOrdenadoDevice;
	int * indicesFitnessOrdenadoDevice;
	float * probabilidadRuletaDevice;
	float * fitnessTotalParaRuletaDevice;
	int * poblacionActualAcambiarDevice;
	int * nuevaPoblacionAcambiarDevice;
	int * individuosAMigrarDevice;
	int * mejorIndividuoGPUDevice;
	float * mejorFitnessIndividuoGPUDevice;
	
// *********************  MATRIZ DE COSTOS  ****************************************	
	//Cargar vector de cordenadas
	float * matrizCostosHost;
	float * matrizCostosDevice;
	float ** vectorDistancias = NULL;
	int tamMatrizCostosDevice = sizeof(float) * cantGenesEnCromosoma * cantGenesEnCromosoma;
	matrizCostosHost = (float*) malloc(tamMatrizCostosDevice);
	vectorDistancias = (float**)malloc(cantGenesEnCromosoma * sizeof(float*));
	for(int i = 0; i < cantGenesEnCromosoma; i++){
		vectorDistancias[i] = (float*)malloc(2 * sizeof(float));
	}
	int res = parserVectorDistancias(vectorDistancias, cantGenesEnCromosoma);
	if (res == -1){
		printf("\nError al leer archivo de coordenadas TCP.\n");
	}else if (res == -2){
		printf("\nError en split de una linea del archivo de coordenadas TCP.\n");
	}else{
		//cargar matriz de costos
		int res = cargarMatrizCostos(matrizCostosHost, vectorDistancias, cantGenesEnCromosoma);
		if (res == -1){
			printf("\nError al cargar la matriz de costos.\n");
		}
	}

	cudaMalloc(&matrizCostosDevice, tamMatrizCostosDevice);
	cudaMemcpy(matrizCostosDevice, matrizCostosHost, tamMatrizCostosDevice, cudaMemcpyHostToDevice);
	
// *********************  MATRIZ DE COSTOS  ****************************************		
	
	
	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitnessFloat);
	cudaMalloc(&fitnessOrdenadoDevice, tamVectorFitnessFloat);
	cudaMalloc(&indicesFitnessOrdenadoDevice, tamVectorFitnessInt);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitnessFloat);
	cudaMalloc(&probabilidadRuletaDevice, tamVectorPoblacionParaRuleta);
	cudaMalloc(&fitnessTotalParaRuletaDevice, tamVectorFitnessRuleta);
	cudaMalloc(&poblacionActualAcambiarDevice, tamVectorFitnessInt);
	cudaMalloc(&nuevaPoblacionAcambiarDevice, tamVectorFitnessInt);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU);
	cudaMalloc(&mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU);
	cudaMalloc(&mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU);

	//Inicio numeros aleatorios.
	int cantHilosRandom = 0;
	if(cantidadPoblacionesGPU > 0){
		cantHilosRandom = cantCromosomasPoblacion / 2 * cantidadPoblacionesGPU;
	}else{
		cantHilosRandom = cantCromosomasPoblacion/ 2 * cantidadPoblacionesCPU;
	}
	struct Random r = initRandom(cantHilosRandom);
	
	//inicializo grid con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGrid(1, cantidadPoblacionesGPU);
	//inicializo bloque con dimension 1 x cantCromosomasPoblacion/2 x 1 hilos
	dim3 tamBlock(1,cantCromosomasPoblacion/2,1);
	//inicializo bloque de migracion con 1 hilo por bloque
	dim3 tamBlockMigracion(1,1,1);

	//inicializacion de la poblacion en GPU
	if(cantidadPoblacionesGPU > 0){
		if(inicializacionAutomatica == 1){
			inicializarPoblacionPermutacion<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice, matrizCostosDevice);
		}else{
			inicializarPoblacionPermutacionManual<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice, matrizCostosDevice);
		}
		//si la selección es por rango
		if(tipoSeleccion == 0){
			ordenarDescendente<<<tamGrid, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
		}
		
		//imprimo poblacion binaria de la gpu y fitness
		if(modoDebug == 1){
			if(imprimirPoblacionEntera == 1){
				//imprimo poblacion binaria de la gpu y fitness
				traerImprimirPermutacion(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, -1);
			}else{
				obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
				//imprimo mejor individuo de la gpu y fitness
				traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, -1);
			}
		}
	}

	static int *individuosAMigrarHostCPU;
	MersenneTwister* mt;
	int tamVectorPoblacionCPU;
	int tamVectorFitnessFloatCPU;
	int tamVectorFitnessIntCPU;
	static int *poblacionActualHostCPU;
	static int *nuevaPoblacionHostCPU;
	static float *fitnessActualHostCPU;
	static float *nuevoFitnessHostCPU;
	static int *indicesFitnessOrdenadoHostCPU;
	static float * fitnessOrdenadoHostCPU;
	int * poblacionActualAcambiarHostCPU;
	int * nuevaPoblacionAcambiarHostCPU;
	float * probabilidadRuletaHostCPU;
	float * fitnessTotalParaRuletaHostCPU;

	//inicializo poblacion en CPU
	if(cantidadPoblacionesCPU > 0){

		tamVectorPoblacionCPU = sizeof(int)*totalGenesCPU;
		tamVectorFitnessFloatCPU = sizeof(float)*totalIndividuosCPU;
		tamVectorFitnessIntCPU = sizeof(int)*totalIndividuosCPU;

		poblacionActualHostCPU = (int *) malloc(tamVectorPoblacionCPU);
		nuevaPoblacionHostCPU = (int *) malloc(tamVectorPoblacionCPU);
		fitnessActualHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		nuevoFitnessHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		individuosAMigrarHostCPU = (int *) malloc(tamVectorIndividuosAMigrarCPU);
		indicesFitnessOrdenadoHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		fitnessOrdenadoHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		poblacionActualAcambiarHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		nuevaPoblacionAcambiarHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		probabilidadRuletaHostCPU = (float *) malloc(tamVectorPoblacionParaRuletaCPU);
		fitnessTotalParaRuletaHostCPU = (float *) malloc(tamVectorFitnessRuletaCPU);

		////INICIALIZO POBLACION EN CPU
		//inicializo la clase mt para las variables aleatorias


		//Inicio numeros aleatorios.
		mt = new MersenneTwister();
		unsigned long seed = time(NULL);
		printf("\n\nSemilla Mersenne Twister: %lu\n\n", seed);
		unsigned long init[1] = {seed}, length = 1;
		mt->init_by_array(init, length);

		//inicializacion de la poblacion en GPU
		if(inicializacionAutomatica == 1){
			inicializarPoblacionPermutacionCPU(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU, matrizCostosHost);
		}else{
			inicializarPoblacionPermutacionCPUManual(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU, matrizCostosHost);
		}
		if(modoDebug == 1){
			if(imprimirPoblacionEntera == 1)
				imprimirPermutacionCPU(fitnessActualHostCPU, tamVectorFitnessFloatCPU, poblacionActualHostCPU, tamVectorPoblacionCPU, -1);
			else{
				obtenerMejoresIndividuosPermutacionCPU(poblacionActualHostCPU,fitnessActualHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
				imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, -1);
			}
		}
		
		//si la selección es por rango
		if(tipoSeleccion == 0){
			for(int i = 0; i < cantidadPoblacionesCPU; i++){
				ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
			}
		}
	}

	int tamVectorIndividuoAMigrarCPUGPU;
	static int *individuoAMigrarCPUaGPUhost;
	static int *individuoAMigrarGPUaCPUhost;
	static int *individuoAMigrarCPUaGPUdevice;
	static int *individuoAMigrarGPUaCPUdevice;
	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		tamVectorIndividuoAMigrarCPUGPU = sizeof(int)*cantGenesEnCromosoma;
		cudaMalloc(&individuoAMigrarCPUaGPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		cudaMalloc(&individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarCPUaGPUhost = (int *) malloc(tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarGPUaCPUhost = (int *) malloc(tamVectorIndividuoAMigrarCPUGPU);
	}

	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	clock_t inicio = clock();
	while(iteracion < numeroGeneraciones){

		if(cantidadPoblacionesGPU > 0){
			//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
			if((iteracion % 2) == 0){
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelPermutacion<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice, iteracion, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice, matrizCostosDevice);
				//si la selección es por rango
				if(tipoSeleccion == 0){
					ordenarDescendente<<<tamGrid, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				if(modoDebug == 1){
					if(imprimirPoblacionEntera == 1)
						traerImprimirPermutacion(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					else{
						obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
						traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
					}
				}
			}else{
			//sino invoco a GPU con nuevaPoblacion, poblacionActual
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelPermutacion<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice, iteracion, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice, matrizCostosDevice);
				//si la selección es por rango
				if(tipoSeleccion == 0){
					ordenarDescendente<<<tamGrid, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				if(modoDebug == 1){
					if(imprimirPoblacionEntera == 1)
						traerImprimirPermutacion(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					else{
						obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
						traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
					}
				}
			}
		}

		//Tiro islas en CPU
		if(cantidadPoblacionesCPU > 0){
			representacionPermutacionCPU(poblacionActualHostCPU, nuevaPoblacionHostCPU, fitnessActualHostCPU, nuevoFitnessHostCPU, poblacionActualAcambiarHostCPU, nuevaPoblacionAcambiarHostCPU, individuosAMigrarHostCPU, mt, iteracion, tamVectorPoblacion, tamVectorFitnessFloat, individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU, mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, matrizCostosHost);
			//si la selección es por rango
			if(tipoSeleccion == 0){
				for(int i = 0; i < cantidadPoblacionesCPU; i++){
					ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
				}
			}
		}
		//Si hay que migrar
		if((generacionesMigrar > 0) && ((iteracion + 1) % generacionesMigrar == 0)){
			if(cantidadPoblacionesGPU > 0 && cantidadPoblacionesCPU > 0){
				//poblaciones en GPU y en CPU
				cudaMemcpy(individuoAMigrarCPUaGPUdevice, individuoAMigrarCPUaGPUhost, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyHostToDevice);
				cudaMemcpy(individuoAMigrarGPUaCPUhost, individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyDeviceToHost);
				if(modoDebug == 1){
					cudaMemcpy(individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);
					printf("\nINDIVIDUOS A MIGRAR EN GPU:\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
						printf("%d", individuosAMigrarHostGPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n\n");
					printf("\nINDIVIDUOS A MIGRAR DE GPU A CPU:\n");

					for(int i = 0; i<cantGenesEnCromosoma; i++){
						printf("%d", individuoAMigrarGPUaCPUhost[i]);
					}
					printf("\n\n");

					printf("\nINDIVIDUOS A MIGRAR EN CPU:\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++){
						printf("%d", individuosAMigrarHostCPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n\n");

					printf("\nINDIVIDUOS A MIGRAR DE CPU A GPU:\n");
					for(int i = 0; i<cantGenesEnCromosoma; i++){
						printf("%d", individuoAMigrarCPUaGPUhost[i]);
					}
					printf("\n\n");
				}
				if((iteracion % 2) == 0){
					migracionRepresentacionPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice, matrizCostosDevice);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN GPU:\n");
						if(imprimirPoblacionEntera == 1)
							traerImprimirPermutacion(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
					migracionRepresentacionPermutacionCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost, matrizCostosHost);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN CPU:\n");
						if(imprimirPoblacionEntera == 1)
							imprimirPermutacionCPU(nuevoFitnessHostCPU, tamVectorFitnessFloatCPU, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacionCPU(nuevaPoblacionHostCPU,nuevoFitnessHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
							imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
						}
					}
				}else{
					migracionRepresentacionPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice, matrizCostosDevice);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN GPU:\n");
						if(imprimirPoblacionEntera == 1)
							traerImprimirPermutacion(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
					migracionRepresentacionPermutacionCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost, matrizCostosHost);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN CPU:\n");
						if(imprimirPoblacionEntera == 1)
							imprimirPermutacionCPU(fitnessActualHostCPU, tamVectorFitnessFloatCPU, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacionCPU(poblacionActualHostCPU,fitnessActualHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
							imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
						}
					}
				}
			}else if(cantidadPoblacionesCPU > 0){
				//poblaciones solamente en CPU
				if((iteracion % 2) == 0){
					migracionRepresentacionPermutacionCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost, matrizCostosHost);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN CPU:\n");
						if(imprimirPoblacionEntera == 1)
							imprimirPermutacionCPU(nuevoFitnessHostCPU, tamVectorFitnessFloatCPU, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacionCPU(nuevaPoblacionHostCPU,nuevoFitnessHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
							imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
						}
					}
				}else{
					migracionRepresentacionPermutacionCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost, matrizCostosHost);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN CPU:\n");
						if(imprimirPoblacionEntera == 1)
							imprimirPermutacionCPU(fitnessActualHostCPU, tamVectorFitnessFloatCPU, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacionCPU(poblacionActualHostCPU,fitnessActualHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
							imprimirMejorIndividuoPermutacionCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
						}
					}
				}
				if(modoDebug == 1){
					printf("\nINDIVIDUOS A MIGRAR EN CPU:\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++){
						printf("%d", individuosAMigrarHostCPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n\n");
				}
			}else{
				//poblaciones solamente en GPU
				if(modoDebug == 1){
					cudaMemcpy(individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);
					printf("\nINDIVIDUOS A MIGRAR EN GPU:\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
						printf("%d", individuosAMigrarHostGPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf(" ");
						}
					}
					printf("\n\n");
				}
				if((iteracion % 2) == 0){
					migracionRepresentacionPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice, matrizCostosDevice);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN GPU:\n");
						if(imprimirPoblacionEntera == 1)
							traerImprimirPermutacion(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
				}else{
					migracionRepresentacionPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice, matrizCostosDevice);
					if(modoDebug == 1){
						printf("\nLUEGO DE MIGRAR EN GPU:\n");
						if(imprimirPoblacionEntera == 1)
							traerImprimirPermutacion(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
				}
			}
		}
		iteracion++;
	}
	printf("Tiempo de ejecucion efectivo: %f s\n", ((double)clock() - inicio) / CLOCKS_PER_SEC);
	if(cantidadPoblacionesGPU > 0){
		if(((iteracion -1) % 2) == 0){
			if(imprimirPoblacionEntera == 1)
				traerImprimirPermutacion(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
			else{
				obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
				traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
			}
		}
		else{
			if(imprimirPoblacionEntera == 1)
				traerImprimirPermutacion(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
			else{
				obtenerMejoresIndividuosPermutacion<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
				traerImprimirMejorIndividuoPermutacion(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
			}
		}
	}

	// free memory for all Cuda malloc
	cudaFree(poblacionActualDevice);
	cudaFree(nuevaPoblacionDevice);
	cudaFree(fitnessActualDevice);
	cudaFree(fitnessOrdenadoDevice);
	cudaFree(indicesFitnessOrdenadoDevice);
	cudaFree(nuevoFitnessDevice);
	cudaFree(probabilidadRuletaDevice);
	cudaFree(fitnessTotalParaRuletaDevice);
	cudaFree(poblacionActualAcambiarDevice);
	cudaFree(nuevaPoblacionAcambiarDevice);
	cudaFree(individuosAMigrarDevice);
	cudaFree(mejorIndividuoGPUDevice);
	cudaFree(mejorFitnessIndividuoGPUDevice);
	cudaFree(matrizCostosDevice);
	
	delete mt;

	deleteRandom(r);

	// free memory for all C malloc
	free(poblacionActualHostGPU);
	free(nuevaPoblacionHostGPU);
	free(fitnessActualHostGPU);
	free(nuevoFitnessHostGPU);
	free(probabilidadRuletaHost);
	free(fitnessTotalParaRuletaHost);
	free(individuosAMigrarHostGPU);
	free(mejorIndividuoCPU);
	free(mejorIndividuoGPU);
	free(mejorFitnessIndividuoCPU);
	free(mejorFitnessIndividuoGPU);
	free(indicesFitnessOrdenadoHost);
	free(matrizCostosHost);
	for(int i = 0; i < cantGenesEnCromosoma; i++){
		free(vectorDistancias[i]);
	}
	free(vectorDistancias);
	

	if(cantidadPoblacionesCPU > 0){
		free(poblacionActualHostCPU);
		free(nuevaPoblacionHostCPU);
		free(fitnessActualHostCPU);
		free(nuevoFitnessHostCPU);
		free(individuosAMigrarHostCPU);
		free(indicesFitnessOrdenadoHostCPU);
		free(fitnessOrdenadoHostCPU);
		free(poblacionActualAcambiarHostCPU);
		free(nuevaPoblacionAcambiarHostCPU);
		free(probabilidadRuletaHostCPU);
		free(fitnessTotalParaRuletaHostCPU);
	}

	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		cudaFree(individuoAMigrarCPUaGPUdevice);
		cudaFree(individuoAMigrarGPUaCPUdevice);
		free(individuoAMigrarCPUaGPUhost);
		free(individuoAMigrarGPUaCPUhost);
	}

	if(strcmp(cudaGetErrorString(cudaPeekAtLastError()), "no error") != 0)
		printf("GPU Error: %s\n\n",cudaGetErrorString(cudaPeekAtLastError()));
	if(strcmp(cudaGetErrorString(cudaThreadSynchronize()), "no error") != 0)
		printf("GPU Error: %s\n\n",cudaGetErrorString(cudaThreadSynchronize()));	
}

#endif
